home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / Apps / ScreenSavers / BackSpaceViews / StarShipView.BackModule / NewSpaceView.m < prev    next >
Text File  |  1995-06-12  |  15KB  |  685 lines

  1. //  NewSpaceView.m
  2. //
  3. //  This class implements the flying starfield screen saver view.
  4. //  This is mostly the code from SpaceView in BackSpace
  5. //
  6. // Hacked by R.S. Brown for added functionality
  7.  
  8.  
  9. #import "NewSpaceView.h"
  10. #import "Thinker.h"
  11. #import "psfuncts.h"
  12. #import "spaceFuncts.h"
  13.  
  14.  
  15.  
  16. #import <dpsclient/wraps.h>
  17. #import <appkit/NXImage.h>
  18. #import <objc/zone.h>
  19. #import <mach/mach.h>
  20. #import <c.h>
  21. #import <libc.h>
  22. #import <math.h>
  23. #import "StarShipView.h"
  24.  
  25. #define PI (3.141592653589)
  26. #define SCREENMOVE 1000      //how often screen moves
  27. #define LIGHTMOVE 10        //how often lights move
  28.  
  29. @implementation NewSpaceView
  30.  
  31. + initialize
  32. {
  33.     if ( self == [NewSpaceView class] )
  34.     {
  35.         // load PostScript procedures SpaceView needs
  36.         // must be careful these are loaded into the correct context
  37.         loadPSProcedures();
  38.     }
  39.     return self;
  40. }
  41.  
  42. //takes theta and distance and stuffs it into x &y for *p
  43. - convertToXY:(STAR *)p
  44. {
  45.  
  46.     p->draw->x = floor(bounds.size.width / 2 + (p->distance * cos(p-> theta)));
  47.     p->draw->y = floor(bounds.size.height / 2 + (p->distance * sin(p-> theta)));
  48.     
  49.     return self;
  50. }
  51.  
  52. - oneStep
  53. {
  54.     int i, stopTest, count;
  55.     int starsInBArray = 0;
  56.     int starsInWArray = 0;
  57.     STAR *p;
  58.     NXPoint *t;
  59.     float adjustedStarSpeed;
  60.     STAR test;
  61.     NXPoint testP;
  62.  
  63.  
  64.  
  65.         if (nstars < NSTARS && !hidden) [self addStar];
  66.  
  67.         stopTest = 0;
  68.         for (i=0; i<nstars; i++)
  69.         {
  70.             p = &stars[i];
  71.             p->distance += (p->delta);
  72.             if(!stopping)
  73.                 adjustedStarSpeed = starSpeed;
  74.             else
  75.                 adjustedStarSpeed = 1.0;
  76.             p->delta = p->delta * p->ddelta * stopDelta * adjustedStarSpeed;
  77.             if(p->delta < .01){ // this must be less than starting p>delta
  78.                 stopTest++;
  79.                 p->delta = 0.0;
  80.             }
  81.  
  82.  
  83.             [self convertToXY:p];
  84.  
  85.             // only draw the star if it moved > 1 pixel or stopped
  86.             if (p->draw->x != p->erase->x || 
  87.                 p->draw->y != p->erase->y || stopped){
  88.                 BOOL mustErase = NO;
  89.                 // add star to the erasure array
  90.                 b[starsInBArray] = *p->erase;
  91.                 bc[starsInBArray] = p->c;
  92.  
  93.                 if (p->distance > p->changepoint[p->changemode]){
  94.                     (p->c)++;    // increment character for next star size
  95.                     (p->changemode)++;
  96.                 }
  97.  
  98.                 // clipping is off, so we must not draw outside view.
  99.                 // replace stars that go too far...
  100.                 if (p->draw->x < smallScreenRect.origin.x+3 ||
  101.                     p->draw->y < smallScreenRect.origin.y+3 ||
  102.                     p->draw->x+7 > smallScreenRect.size.width +
  103.                     smallScreenRect.origin.x - 3 ||
  104.                     p->draw->y+7 > smallScreenRect.size.height +
  105.                     smallScreenRect.origin.y - 3){
  106.                     
  107.                     [self replaceStarAt:i];
  108.                     //printf("replaced star at %d\n",i);
  109.                     mustErase = YES;
  110.                 }
  111.  
  112.                 w[starsInWArray] = *p->draw;
  113.                 wc[starsInWArray] = p->c;
  114.  
  115.                 if(mustErase){
  116.                     testP.x = b[starsInBArray].x;
  117.                     testP.y = b[starsInBArray].y;
  118.                     test.erase = &testP;
  119.                     if ([self allowBStars:&test])starsInBArray++;
  120.                 }
  121.                 else if ([self allowBStars:p])starsInBArray++;
  122.                 if ([self allowWStars:p]) starsInWArray++;
  123.  
  124.                     
  125.                     
  126.                 t = p->draw; p->draw = p->erase; p->erase = t;
  127.             }
  128.         }
  129.  
  130.         bc[starsInBArray] = wc[starsInWArray] = 0;    //null terminate string
  131.         if (starsInBArray){
  132.             for (i=0; i<(starsInBArray-1); i++){
  133.                 bOffsets[i].x = b[i+1].x - b[i].x;
  134.                 bOffsets[i].y = b[i+1].y - b[i].y;
  135.             }
  136.             bOffsets[i].x = bOffsets[i].y  = 0;
  137.  
  138.             count = 0;
  139.             while (count < starsInBArray){
  140.                 char tc;
  141.                 int j;
  142.                 // You get the best performance if you put out all the stars
  143.                 // at once.  This causes noticable flicker, so I put out 
  144.                 // 100 of the stars per iteration.  This gives reasonable speed
  145.                 // and flicker is hardly noticable.  Besides, stars
  146.                 // _should_ flicker a little...
  147.         
  148.                 int t = (starsInBArray - count);
  149.                 i = (t < STARSPERIT)?t:STARSPERIT;
  150.                 j = i + count;
  151.             
  152.                 PSsetgray(NX_BLACK);
  153.                 tc = bc[j]; bc[j] = 0;
  154.                 PSWXYShow(b[count].x, b[count].y, &bc[count], 
  155.                 (float *)(&bOffsets[count].x), i*2);
  156.                 bc[j] = tc;
  157.                 count += STARSPERIT;
  158.  
  159.             }
  160.         }
  161.  
  162.         if (starsInWArray){
  163.             for (i=0; i<(starsInWArray-1); i++){
  164.                 wOffsets[i].x = w[i+1].x - w[i].x;
  165.                 wOffsets[i].y = w[i+1].y - w[i].y;
  166.             }
  167.             wOffsets[i].x = wOffsets[i].y  = 0;
  168.  
  169.             count = 0;
  170.             while (count < starsInWArray){
  171.                 char tc;
  172.                 int j;
  173.                 // You get the best performance if you put out all the stars
  174.                 // at once.  This causes noticable flicker, so I put out 
  175.                 // 100 of the stars per iteration.  This gives reasonable speed
  176.                 // and flicker is hardly noticable.  Besides, stars
  177.                 // _should_ flicker a little...
  178.         
  179.                 int t = (starsInWArray - count);
  180.                 i = (t < STARSPERIT)?t:STARSPERIT;
  181.                 j = i + count;
  182.             
  183.  
  184.                 PSsetgray(NX_WHITE);
  185.                 tc = wc[j]; wc[j] = 0;
  186.                 PSWXYShow(w[count].x, w[count].y, &wc[count], 
  187.                  (float *)(&wOffsets[count].x), i*2);
  188.                 wc[j] = tc;
  189.                 count += STARSPERIT;
  190.             }
  191.         }        
  192.         if((stopTest >= (nstars - 1)) && stopping && !stopped){
  193.             stopped = YES;
  194.             [bodyController starsStopped];
  195.         }
  196.     screenChangeTime++;
  197.     if(screenChangeTime > SCREENMOVE){
  198.         [self drawScreen];
  199.         [starShip viewScreenResized:&smallScreenRect];
  200.     }
  201.  
  202.     lightChangeTime++;
  203.     if(lightChangeTime > LIGHTMOVE){
  204.         lightChangeTime = 0;
  205.         [self drawScreenLights];
  206.  
  207.     }
  208.     return self;
  209. }
  210.  
  211.  
  212.  
  213. // make the actual image 8 pixels bigger in each direction
  214. // then make the avoidance rect -8 pixels to width and height of image
  215. //for example 
  216. // if the image you want to avoid is 5 pixels square
  217. // make image 21 X 21 with a black background and
  218. //center the actual image. make the avoidance rect
  219. //0,0,13,13
  220. //The reason for this is that the stars are a max of
  221. //7 X 7 pixels and drawn from lower left corner
  222.  
  223. - (BOOL) allowWStars:(const STAR *)p
  224. {
  225. // avoidance rectangles are set by message from body objects
  226.  
  227. int ii;
  228. NXRect avoid;
  229. BOOL allow;
  230.  
  231.     allow = YES;
  232.     // return yes if point is outside of all avoidance rectangles
  233.     for(ii=0;ii < [avoidStorage count];ii++){
  234.             avoid = ((AvoidStruct *)[avoidStorage elementAt:ii])->avoid;
  235.             
  236.             // just return if voidRect not set
  237.             if ((!avoid.size.width) ||
  238.                 p->draw->x < avoid.origin.x ||
  239.                 p->draw->y < avoid.origin.y ||
  240.                 p->draw->x > avoid.origin.x+avoid.size.width ||
  241.                 p->draw->y > avoid.origin.y+avoid.size.height){
  242.                 
  243.             }
  244.             else
  245.                 allow = NO;    
  246.     }    
  247.     return allow;
  248. }
  249.  
  250.  
  251. //check the ones that need erasing
  252. //returns yes if outside avoidance rect
  253. - (BOOL) allowBStars:(const STAR *)p
  254. {
  255. int ii;
  256. NXRect avoid;
  257. BOOL allow;
  258.  
  259.     allow = YES;
  260.     // return yes if point is outside of all avoidance rectangles
  261.     for(ii=0;ii < [avoidStorage count];ii++){
  262.             avoid = ((AvoidStruct *)[avoidStorage elementAt:ii])->avoid;
  263.             
  264.             // just return if voidRect not set
  265.             if ((!avoid.size.width) ||
  266.                 p->erase->x < avoid.origin.x ||
  267.                 p->erase->y < avoid.origin.y ||
  268.                 p->erase->x > avoid.origin.x+avoid.size.width ||
  269.                 p->erase->y > avoid.origin.y+avoid.size.height){
  270.                 
  271.             }
  272.             else
  273.                 allow = NO;    
  274.     }    
  275.     return allow;
  276. }
  277.  
  278.  
  279. - initFrame:(const NXRect *)frameRect
  280. {
  281.  
  282.     [super initFrame:frameRect];
  283.     // these were in original code - I don't know if they do anything
  284.     
  285.     //[self allocateGState];        // For faster lock/unlockFocus
  286.     //[self setClipping:NO];        // even faster...
  287.  
  288.     loadPSProcedures();
  289.     loadSpaceProcedures();
  290.     PSWDefineFont("StarFont");
  291.     PSselectfont("StarFont", 1.0);
  292.  
  293.     // not really needed
  294.     // for completeness - view gets resized right away by sizeTo method
  295.     [self setSmallScreenRect:frameRect];
  296.     [self setRadius];
  297.  
  298.     screenChangeTime = 0;
  299.     screenResizeLimit = 0;
  300.     screenResizeDir = 1;
  301.     lightChangeTime = 0;
  302.     lightLimit = 16;
  303.     stopDelta = 1.0;
  304.     stopped = NO;
  305.     stopping = NO;
  306.     hidden = NO;
  307.     
  308.  
  309.     lightInc = floor(smallScreenRect.size.width/60);
  310.     lights.l_x_pos = smallScreenRect.size.width/2 - 4 +
  311.      smallScreenRect.origin.x;
  312.     lights.r_x_pos = smallScreenRect.size.width/2 + 4 +
  313.     smallScreenRect.origin.x;
  314.     lights.y_pos = floor(smallScreenRect.origin.y/2);
  315.  
  316.     [self setStarsStopped];     //set stars stopped at the beginning
  317.  
  318.     return self;
  319. }
  320.  
  321. - drawSelf:(const NXRect *)rects :(int)rectCount
  322. {
  323.     // this drawself doesn't really draw the view at all.
  324.     // in fact it just promotes the window to screen depth...
  325.  
  326.     NXRect t = {0,0,1,1};
  327.  
  328.     PSsetrgbcolor(.333,.222,.111);
  329.     NXRectFill(&t);    //yucky trick for window depth promotion!
  330.     PSsetgray(NX_BLACK); NXRectFill(&t);
  331.     PSselectfont("StarFont", 1.0);
  332.  
  333.     return self;
  334. }
  335.  
  336. -     setSmallScreenRect:(const NXRect *)newRect;
  337. {
  338.     //sets variable to bounds of the small window in the viewscreen rect
  339.  
  340.     smallScreenRect.size.width = newRect->size.width;
  341.     smallScreenRect.size.height = newRect->size.height;
  342.     smallScreenRect.origin.x = newRect->origin.x;
  343.     smallScreenRect.origin.y = newRect->origin.y;
  344.  
  345.     return self;
  346. }
  347. - sizeTo:(NXCoord)width :(NXCoord)height
  348. {
  349. NXSize delta;
  350.  
  351.     [super sizeTo:width :height];
  352.  
  353.     delta.width = floor(width * .15);
  354.     delta.height = floor(height * .15);
  355.  
  356.     smallScreenRect.size.width = width - delta.width;
  357.     smallScreenRect.size.height = height - delta.height;
  358.     smallScreenRect.origin.x = (delta.width / 2);
  359.     smallScreenRect.origin.y = (delta.height / 2);
  360.  
  361.     screenChangeTime = SCREENMOVE;  //put them on screen
  362.     lightChangeTime = LIGHTMOVE;
  363.     lightLimit = 16;
  364.  
  365.     lightInc = floor(smallScreenRect.size.width/60);
  366.     lights.l_x_pos = smallScreenRect.size.width/2 - 4 +
  367.      smallScreenRect.origin.x;
  368.     lights.r_x_pos = smallScreenRect.size.width/2 + 4 +
  369.     smallScreenRect.origin.x;
  370.     lights.y_pos = floor(smallScreenRect.origin.y/2);
  371.  
  372.     if (oldSize.width != bounds.size.width ||
  373.             oldSize.height != bounds.size.height)
  374.     {
  375.         oldSize.width = bounds.size.width;
  376.         oldSize.height = bounds.size.height;
  377.  
  378.         nstars = 0;
  379.         [self setRadius];
  380.         [self display];
  381.         [starShip viewScreenResized:&smallScreenRect];
  382.         
  383.     }
  384.     
  385.     return self;
  386. }
  387.  
  388. // only call addStar if there is room in the stars array!
  389. - addStar
  390. {
  391.     [self replaceStarAt:nstars++];
  392.     return self;
  393. }
  394.  
  395. - replaceStarAt:(int)index
  396. {
  397.     float dist, t;
  398.     int tries = 0;
  399.     STAR *p = &stars[index];
  400.     BOOL inBounds;
  401.  
  402.     p->draw = &p->r1;
  403.     p->erase = &p->r2;
  404.     
  405.  
  406.         do {
  407.             p->theta = randBetween(0,(2*PI));
  408.  
  409.         if (tries++ < 3) p->distance = randBetween(1, radius);
  410.         else p->distance = randBetween(1, p->distance);
  411.  
  412.         inBounds = YES;
  413.         [self convertToXY:p];
  414.  
  415.         if (p->draw->x < smallScreenRect.origin.x+3 ||
  416.             p->draw->y < smallScreenRect.origin.y+3 ||
  417.             p->draw->x + 7 > smallScreenRect.size.width +
  418.             smallScreenRect.origin.x - 3 ||
  419.             p->draw->y + 7 > smallScreenRect.size.height +
  420.             smallScreenRect.origin.y - 3)
  421.         {
  422.             inBounds = NO;
  423.         }
  424.     } while (!inBounds);
  425.         //take out
  426.     if(stopping){
  427.         p->delta = 0.0;
  428.         p->ddelta = 1.0;
  429.     }
  430.     else{
  431.         p->delta = .2;
  432.         //p->ddelta = randBetween(1.0, 1.1);
  433.         p->ddelta = randBetween(1.0, 1.05);
  434.  
  435.     }
  436.  
  437.     t = randBetween(0, (0.42*radius));
  438.  
  439.     dist = MAX(20,t);
  440.     p->changepoint[0] = p->distance + 5;            // to b
  441.     p->changepoint[1] = p->changepoint[0] - 5 + dist + dist;    // to c
  442.     p->changepoint[2] = p->changepoint[1] + dist;    // to d
  443.     p->changepoint[3] = p->changepoint[2] + dist;    // to e
  444.     p->changepoint[4] = p->changepoint[3] + dist;    // to f
  445.     p->changepoint[5] = 100000;                        // never change to g
  446.  
  447.     p->changemode = 0;
  448.     
  449.     if ((++toggle) & 1) p->c = 'a';
  450.     else p->c = 'g';
  451.  
  452.     p->r2 = p->r1;
  453.  
  454.     return self;
  455. }
  456.  
  457. - drawScreen
  458. {
  459.     screenChangeTime = 0;
  460.  
  461.     PSscreenBorder(smallScreenRect.origin.x,
  462.         smallScreenRect.origin.y,
  463.         smallScreenRect.size.width + smallScreenRect.origin.x,
  464.         smallScreenRect.size.height + smallScreenRect.origin.y,
  465.     smallScreenRect.origin.y-2.5,5.0,0.0);
  466.  
  467.     if(screenResizeDir == 1){
  468.         smallScreenRect.size.width++;
  469.         smallScreenRect.size.height++;
  470.         smallScreenRect.origin.x--;
  471.         smallScreenRect.origin.y--;
  472.     }
  473.     else{
  474.         smallScreenRect.size.width--;
  475.         smallScreenRect.size.height--;
  476.         smallScreenRect.origin.x++;
  477.         smallScreenRect.origin.y++;
  478.     }    
  479.  
  480.     PSscreenBorder(smallScreenRect.origin.x,
  481.         smallScreenRect.origin.y,
  482.         smallScreenRect.size.width + smallScreenRect.origin.x,
  483.         smallScreenRect.size.height + smallScreenRect.origin.y,
  484.     smallScreenRect.origin.y-2.5,5.0,1.0);
  485.     
  486.     screenResizeLimit++;
  487.     if(screenResizeLimit > 7){
  488.         if(screenResizeDir)
  489.          screenResizeDir = 0;
  490.         else
  491.           screenResizeDir = 1;    //reverse the values
  492.         screenResizeLimit = 0;
  493.     }
  494.     
  495.     return self;            
  496. }
  497.  
  498.  
  499. - drawScreenLights
  500. {
  501. float red,green,blue;
  502.  
  503.         PSscreenLights(lights.l_x_pos,lights.r_x_pos,
  504.     lights.y_pos,0.0,0.0,0.0);
  505.     
  506.     lights.l_x_pos -= lightInc;
  507.     lights.r_x_pos += lightInc;
  508.  
  509.     lightLimit++;
  510.     if(lightLimit > 15){
  511.         lightLimit = 0;
  512.  
  513.     lights.l_x_pos = smallScreenRect.size.width/2 - 4 +
  514.      smallScreenRect.origin.x;
  515.     lights.r_x_pos = smallScreenRect.size.width/2 + 4 +
  516.     smallScreenRect.origin.x;
  517.     lights.y_pos = smallScreenRect.origin.y/2;
  518.     }
  519.     red = randBetween(0.1,1.0);  //make sure never black
  520.     green = randBetween(0.0,1.0);
  521.     blue = randBetween(0.0,1.0);
  522.  
  523.     PSscreenLights(lights.l_x_pos,lights.r_x_pos,
  524.     lights.y_pos,red,green,blue);
  525.         return self;            
  526. }
  527.  
  528. //this causes stars to slow down and stop - continue being displayed
  529. - stopStars
  530. {
  531.     // needs to be < 1 so that the stars stop eventually
  532.     // this will control rate of stoppage
  533.     stopDelta = 0.90;
  534.     stopping = YES;
  535.     return self;
  536.  
  537. }
  538. // causes stars to disappear altogether - 
  539. //only works if stars are stopped
  540. //send a setStarsStopped message first
  541. - hideStars
  542. {
  543.     [self setStarsStopped];
  544.     PSsetgray(0);
  545.     NXRectFill(&smallScreenRect);
  546.     nstars = 0;
  547.     hidden = YES;
  548.     return self;
  549. }
  550.  
  551.  
  552.  
  553. - startStars
  554. {
  555. STAR *p;
  556. int ii;
  557.     hidden = NO;
  558.     for (ii=0; ii < NSTARS; ii++){
  559.         p = &stars[ii];
  560.         p->delta = 0.2;
  561.     }
  562.     stopDelta = 1.0;
  563.     stopping = NO;
  564.     stopped = NO;
  565.     return self;
  566. }
  567.  
  568. - (BOOL)isStopped
  569. {
  570.     return stopped;
  571. }
  572. - (BOOL)isStopping
  573. {
  574.     return stopping;
  575. }
  576.  
  577. - setRadius
  578. {
  579.     float x = smallScreenRect.size.width;
  580.     float y = smallScreenRect.size.height;
  581.     radius = (sqrt(x*x + y*y))/2;
  582.     return self;
  583. }
  584.  
  585. - (const char *)windowTitle
  586. {
  587.     return "The Final Frontier";
  588. }
  589.  
  590. - setAvoidRect:(Storage *)storage    
  591. {
  592.     avoidStorage = storage;
  593.     return self;
  594. }
  595.  
  596. - didLockFocus
  597. {
  598.     PSselectfont("StarFont", 1.0);
  599.     return self;
  600. }
  601.  
  602. - (BOOL)useBufferedWindow
  603. {    return NO;
  604. }
  605.  
  606. - inspector:sender
  607. {
  608.     return [sender spaceInspector];
  609. }
  610.  
  611. - (BOOL)ignoreMouseMovement
  612. {    return NO;
  613. }
  614.  
  615. - inspectorWillBeRemoved
  616. {    return self;    // just a prototype
  617. }
  618.  
  619. - inspectorInstalled
  620. {    return self;    // just a prototype
  621. }
  622.  
  623. - setStarShipOutlet:(id)outlet
  624. {
  625.     starShip = outlet;
  626.     return self;
  627. }
  628. - setBodyControllerOutlet:(id)outlet
  629. {
  630.     bodyController = outlet;
  631.     return self;
  632. }
  633. - setStarSpeed:sender
  634. {
  635.     starSpeed = [sender floatValue];
  636.     return self;     
  637. }
  638.  
  639. //this sets stars instantly stopped
  640. - setStarsStopped
  641. {
  642. STAR *p;
  643. int ii;
  644.     nstars = 0;
  645.     for(ii=0;ii < NSTARS;ii++){
  646.         [self addStar];
  647.         p = &stars[ii];
  648.         p->delta = 0.0;
  649.     }
  650.     
  651.     stopping = YES;
  652.     stopped = YES;
  653.     return self;
  654. }
  655.  
  656. @end
  657.  
  658.  
  659. @implementation View(nonretainedFillMethod)
  660.  
  661. // I add this method as a category of View to be sure that all
  662. // my views implement it.  I really want to use nonretained windows
  663. // but they are drawn via drawSelf at all kinds of goofy times.  It
  664. // seems like the kit kind of throws up its hands when it doesn't have
  665. // a buffer to draw from, so you get a lot more drawSelfs than you need,
  666. // and sometimes you don't get them when you really want them.  I know
  667. // when I need the background filled in black, so I factor that out of
  668. // my drawSelf and then drawself only draws things that are already on
  669. // screen so you don't see it happen.  I will only call this method on
  670. // a nonretained (and full screen) window.
  671.  
  672. - fillBoundsWithBlack
  673. {
  674.     if ([self canDraw])
  675.     {
  676.         [self lockFocus];
  677.         PSsetgray(NX_BLACK);
  678.         NXRectFill(&bounds);
  679.         [self unlockFocus];
  680.     }
  681.     return self;
  682. }
  683.  
  684. @end
  685.